/*
 * This is a slightly modified version of glpng.
 * First, if correcty incompatibility with libpng >= 1.4,
 * and second, it allows to read from a file in memory instead
 * of from a FILE*, fmemopen() being non-standard at the moment.
 */

/* PNG loader library for OpenGL v1.45 (10/07/00)
 * by Ben Wyatt ben@wyatt100.freeserve.co.uk
 * Using LibPNG 1.0.2 and ZLib 1.1.3
 *
 * This software is provided 'as-is', without any express or implied warranty.
 * In no event will the author be held liable for any damages arising from the
 * use of this software.
 *
 * Permission is hereby granted to use, copy, modify, and distribute this
 * source code, or portions hereof, for any purpose, without fee, subject to
 * the following restrictions:
 *
 * 1. The origin of this source code must not be misrepresented. You must not
 *    claim that you wrote the original software. If you use this software in
 *    a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered versions must be plainly marked as such and must not be
 *    misrepresented as being the original source.
 * 3. This notice must not be removed or altered from any source distribution.
 */

#include <utility/gltexture.h>
#ifndef GL_CLAMP_TO_EDGE
#define GL_CLAMP_TO_EDGE 0x812F
#endif

static GLint MaxTextureSize = 0;

/* Returns a safe texture size to use (ie a power of 2), based on the current texture size "i" */
static int SafeSize(int i) 
{
	int p;

	if (i > MaxTextureSize)
		return MaxTextureSize;

	for (p = 0; p < 24; p++)
		if (i <= (1 << p))
			return 1 << p;

	return MaxTextureSize;
}

/* Resize the texture since gluScaleImage doesn't work on everything */
static void Resize(
	int components, 
	const png_bytep d1, 
	int w1, 
	int h1, 
	png_bytep d2, 
	int w2, 
	int h2) 
{
	const float sx = (float) w1/w2, sy = (float) h1/h2;
	int x, y, xx, yy, c;
	png_bytep d;

	for (y = 0; y < h2; y++) 
	{
		yy = (int) (y*sy)*w1;

		for (x = 0; x < w2; x++) 
		{
			xx = (int) (x*sx);
			d = d1 + (yy+xx)*components;

			for (c = 0; c < components; c++)
				*d2++ = *d++;
		}
	}
}

static GLuint SetParams(
	int wrapst, 
	int magfilter, 
	int minfilter) 
{
	GLuint id;

	glGenTextures(1, &id);
	glBindTexture(GL_TEXTURE_2D, id);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapst);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapst);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magfilter);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minfilter);

	return id;
}

GLuint GLLoadPng(FILE* f, unsigned int* w, unsigned int* h) 
{

	GLint pack, unpack;
	png_byte header[8];
	png_structp png;
	png_infop info;
	png_infop endinfo;
	png_bytep data, data2;
	png_bytep *row_p;
	//png_dat d;
	//char* ptr;
	png_uint_32 width, height, rw, rh;
	int depth, color;
	png_uint_32 i;
	GLuint texid;

	GLenum glformat;
	GLint glcomponent;

	fread(header, 1, 8, f);

	if((header != NULL)&& (png_sig_cmp(header, 0, 8)))
	{
		fprintf(stderr, "Png Error 2\n");
		return 0; /* Changed from the original to march png >= 1.4 */
	}

	png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	info = png_create_info_struct(png);
	endinfo = png_create_info_struct(png);

	if(setjmp(png_jmpbuf(png)))
	{
		png_destroy_read_struct(&png, &info, &endinfo);
		fprintf(stderr, "Png Error 3\n");
		return 0;
	}
	
	png_init_io(png, f);
	png_set_sig_bytes(png, 8);
	png_read_info(png, info);
	png_get_IHDR(png, info, &width, &height, &depth, &color, NULL, NULL, NULL);

	*w = width;
	*h = height;

	if(MaxTextureSize == 0)
		glGetIntegerv(GL_MAX_TEXTURE_SIZE, &MaxTextureSize);

	if (color == PNG_COLOR_TYPE_GRAY || color == PNG_COLOR_TYPE_GRAY_ALPHA)
		png_set_gray_to_rgb(png);

	png_read_update_info(png, info);

	data  = (png_bytep)calloc(height, png_get_rowbytes(png, info));//mcalloc in origin
	row_p = (png_bytep*)calloc(height, sizeof(png_bytep));

	for (i = 0; i < height; i++)
		row_p[i] = &data[png_get_rowbytes(png, info)*i];

	png_read_image(png, row_p);
	free(row_p);//mfree in origin

	rw = SafeSize(width);
	rh = SafeSize(height);

	if (rw != width 
	 || rh != height)
	{
		const int channels = png_get_rowbytes(png, info) / width;

		data2 = (png_bytep)calloc(rw*rh, channels);//mcalloc in origin
		Resize(channels, data, width, height, data2, rw, rh);

		width = rw;
		height = rh;
		
		free(data);//mfree in origin
		data = data2;
	}

	/* OpenGL stuff */	
	if((texid = SetParams(GL_CLAMP_TO_EDGE, GL_LINEAR, GL_LINEAR)) == 0)
	{
		fprintf(stderr, "Png Error 4\n");
		return 0;
	}

	glGetIntegerv(GL_PACK_ALIGNMENT, &pack);
	glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack);
	glPixelStorei(GL_PACK_ALIGNMENT, 1);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	switch (color) 
	{
		case PNG_COLOR_TYPE_GRAY:
		case PNG_COLOR_TYPE_RGB:
		case PNG_COLOR_TYPE_PALETTE:
			glformat = GL_RGB;
			glcomponent = 3;
			//pinfo->Alpha = 0;
			break;

		case PNG_COLOR_TYPE_GRAY_ALPHA:
		case PNG_COLOR_TYPE_RGB_ALPHA:
			glformat = GL_RGBA;
			glcomponent = 4;
			//pinfo->Alpha = 8;
			break;

		default:
			glDeleteTextures(1, &texid);
			//puts("glformat not set");
			fprintf(stderr, "Png Error glformat not set\n");
			return 0;
	}

	glTexImage2D(GL_TEXTURE_2D, 0, glcomponent, width, height, 0, 
	             glformat, GL_UNSIGNED_BYTE, data);

	glPixelStorei(GL_PACK_ALIGNMENT, pack);
	glPixelStorei(GL_UNPACK_ALIGNMENT, unpack);
	
	/* OpenGL end */
	png_read_end(png, endinfo);
	png_destroy_read_struct(&png, &info, &endinfo);

	free(data);//mfree in origin
	fprintf(stdout, "png texid: %d\n", texid);
	return texid;
}

